﻿using System;
using UnityEngine;
using System.Collections;
using System.IO;
using System.Text;
using LitJson;
using System.Collections.Generic;

namespace QPEngine
{
    public class Main : MonoBehaviour
    {

        /// <summary>
        /// 调试模式
        /// </summary>
        public bool DebugMode = false;
        /// <summary>
        /// 检测webUrl地址消耗了多少时间
        /// </summary>
        public float urlexpend = 0f;

        /// <summary>
        /// 记录所有可能连接成功的线路
        /// </summary>
        public List<List<string>> urlsucceed = new List<List<string>>();

        /// <summary>
        /// 记录最终检测的几条线路
        /// </summary>
        public List<string> urls = new List<string>();

        /// <summary>
        /// 当前检测的线路个数
        /// </summary>
        private int urlCount = 0;

        public  Action<int,int> Progress;

        /// <summary>
        /// 初始化游戏管理器
        /// </summary>
        void Start()
        {
            Log.Debug($"游戏启动{TimeHelper.Format()}");
            Application.runInBackground = true;
            AppConst.DebugMode = this.DebugMode;

            OnVersion();

            StartCoroutine(OnInitConfiger());

        }


        /// <summary>
        /// 本地版本检测,防止APP升级引发异常
        /// </summary>
        public void OnVersion()
        {
            string path = PathHelp.AppHotfixResPath + AppConst.InstallFileName;
            if (!File.Exists(path))
            {
                //写入安装程序时间
                AppConst.appInstallConfiger.installTime = TimeHelper.ToNumber().ToString();
                AppConst.appInstallConfiger.installVersion = Application.version;
                return;
            }
            string json = File.ReadAllText(path);
            AppInstallConfiger ac = JsonMapper.ToObject<AppInstallConfiger>(json);

            if (!ac.installVersion.Equals(Application.version))
            {
                Directory.Delete(PathHelp.AppHotfixResPath, true);
                AppConst.appInstallConfiger.installTime = TimeHelper.ToNumber().ToString();
                AppConst.appInstallConfiger.installVersion = Application.version;
            }
        }

        /// <summary>
        /// 初始化配置
        /// </summary>
        public IEnumerator OnInitConfiger()
        {
            string configerPath = PathHelp.AppHotfixResPath + AppConst.CSConfigerName;
            string json = string.Empty;
            if (!File.Exists(configerPath))
            {
                Log.Error($"热更新{AppConst.CSConfigerName}加载失败!");
                configerPath = PathHelp.AppResPath + AppConst.CSConfigerName;
            }

            if (Application.platform == RuntimePlatform.Android)
            {
                WWW www = new WWW(configerPath);
                yield return www;
                json = www.text;
            }
            else
            {
                json = File.ReadAllText(configerPath);
            }
            if (!json.Contains(AppConst.ConstCode)) { json = MD5Helper.DecryptDES(json); }
            Debug.LogError(json);
            AppConst.csConfiger = JsonMapper.ToObject<CSConfiger>(json);

            StartCoroutine(OnExtract());
        }

        /// <summary>
        /// 解压资源,将资源拷贝到可以更新的目录
        /// </summary>
        public IEnumerator OnExtract()
        {
            //程序内部资源路径
            string innerPaht = PathHelp.AppResPath;

            //热更新目录路径
            string hotfixPath = PathHelp.AppHotfixResPath;

            string ExistsPath = PathHelp.AppHotfixResPath + AppConst.InstallFileName;

            Debug.LogError(ExistsPath);
            if (File.Exists(ExistsPath))
            {
                OnFindWebUrl();
                yield break;
            }

            if (!File.Exists(ExistsPath)) { Directory.CreateDirectory(PathHelp.AppHotfixResPath); }
            
            string innerFile = innerPaht + AppConst.UpdateConfigerName;
            string outfile = hotfixPath + AppConst.UpdateConfigerName;
            
            if (File.Exists(outfile)) { File.Delete(outfile); }
            
            if (Application.platform == RuntimePlatform.Android)
            {
                WWW www = new WWW(innerFile);
                yield return www;

                if (www.isDone)
                {
                    File.WriteAllBytes(outfile, www.bytes);
                }
                yield return 0;
            }
            else
            {
                File.Copy(innerFile, outfile, true);
            }

            yield return new WaitForEndOfFrame();

            //释放所有文件到数据目录
            string txt = File.ReadAllText(outfile);
            UpdateConfiger updateConfiger = new UpdateConfiger();
            updateConfiger = JsonMapper.ToObject<UpdateConfiger>(txt);
            BaseValueConfigerJson[] bfj = updateConfiger.JsonData;
            
            for (int i=0;i<bfj.Length;i++)
            {
                Progress?.Invoke(bfj.Length, i);
                innerFile = innerPaht + bfj[i].dirPath;
                outfile = hotfixPath + bfj[i].dirPath;

                string dir = Path.GetDirectoryName(outfile);

                if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); }
                
                if (Application.platform == RuntimePlatform.Android)
                {
                    WWW www = new WWW(innerFile);
                    yield return www;

                    if (www.isDone)
                    {
                        File.WriteAllBytes(outfile, www.bytes);
                    }
                    yield return new WaitForEndOfFrame();
                }
                else
                {
                    if (File.Exists(outfile))
                    {
                        File.Delete(outfile);
                    }
                    File.Copy(innerFile, outfile, true);
                }
                yield return new WaitForEndOfFrame();
            }
            OnFindWebUrl();
        }

        /// <summary>
        /// 找出所有通向服务器的web线路
        /// </summary>
        public void OnFindWebUrl()
        {
            string prot = string.Empty;
            urlsucceed.Clear();
            for (int j = 0; j < AppConst.DNS.Length; j++)
            {
                List<string> webUrl = new List<string>();
                for (int i = 0; i < AppConst.ResourcePortRange; i++)
                {
                    webUrl.Add(AppConst.DNS[j] + prot);
                    prot = ":" + (AppConst.ResourcePort + i);
                }
                urlsucceed.Add(webUrl);
            }
            StartCoroutine(CheckLinkState(urlsucceed));
        }

        /// <summary>
        ///  检测线路状态
        /// </summary>
        /// 
        public IEnumerator CheckLinkState(List<List<string>> liks)
        {
            //找出可以用并且最快的一条线路
            List<UrlInfo> WWWUrlyesList = new List<UrlInfo>();

            ExecuteCheckLink(liks[urlCount], WWWUrlyesList);

            bool WWWUrlok = false;
            while (!WWWUrlok)
            {
                if (urlexpend > AppConst.DnsCheckOutTime)
                {
                    // Print.LogError(string.Format("获取线路{0}超时", AppConst.DNS[CurrentCheckNum]), LogLevel.Max);
                    urlCount++;
                    urlexpend = 0;
                    if (urlCount >= AppConst.DNS.Length)
                    {
                        WWWUrlok = true;
                        //Print.LogError(string.Format("获取所有线路超时"), LogLevel.Max);
                        StartCoroutine(GetAppInfoConfiger(string.Empty));
                        StartCoroutine(GetValueConfiger(string.Empty));
                        StartCoroutine(OnUpdateFailed("更新资源出错"));
                        yield break;
                    }
                    else
                    {
                        ExecuteCheckLink(liks[urlCount], WWWUrlyesList);
                    }
                }

                yield return new WaitForSeconds(0.1f);
                urlexpend += 0.1f;

                if (WWWUrlyesList.Count > 0)
                {
                    //Util.Log("C# 检测线路结束==" + Util.TickCount, 10);
                    AppConst.DNS = AppConst.DNS.SortConst(urlCount);
                    WWWUrlok = true;
                    UrlInfo uInfo = (UrlInfo)WWWUrlyesList[0];
                    //进行配置下载
                    StartCoroutine(GetUpdateConfiger(uInfo));
                    StartCoroutine(GetAppInfoConfiger(uInfo.url));
                    StartCoroutine(GetValueConfiger(uInfo.url));
                    yield break;
                }
            }
            yield break;
        }


        public void ExecuteCheckLink(List<string> liks, List<UrlInfo> WWWUrlyesList)
        {
            for (int i = 0; i < liks.Count; i++)
            {
                string link = liks[i] + Util.ServerUpdateConfigerPath;
                urls.Add(link);
                StartCoroutine(CSCheckWebUrl(link, WWWUrlyesList));
            }

        }


        public IEnumerator CSCheckWebUrl(string _url, List<UrlInfo> act)
        {
            if (act.Count > 0) { yield break; }
            UrlInfo ui = new UrlInfo();
            ui.url = _url;
            string url = _url + "?v=" + TimeHelper.ToNumber();
            string html = string.Empty;
            WWW www = new WWW(url);
            yield return www;
            if (www.isDone && string.IsNullOrEmpty(www.error))
            {
                if (www.text.Contains(AppConst.AuthCode))
                {
                    ui.conten = www.text;
                    act.Add(ui);
                }
                www.Dispose();
                yield break;
            }
            www.Dispose();
            yield break;
        }

        /// <summary>
        /// 启动更新下载
        /// </summary>
        IEnumerator OnUpdateResource()
        {
            string url = AppConst.WebUrl;

            UpdateConfiger updateConfiger = AppConst.updateConfiger;

            List<string> HotUpdateFiles = new List<string>();
            string dataPath = PathHelp.AppHotfixResPath;
            if (!Directory.Exists(dataPath))
            {
                Directory.CreateDirectory(dataPath);
               // QPDefine.CalliOS.SetNoBackupFlag(dataPath);

            }
            //检测版本是否需要更新
            int webHallLuaVer = updateConfiger.Version;
            int localHallLuaVer = AppConst.appInstallConfiger.updateVersion;
            if (webHallLuaVer == localHallLuaVer)
            {
                OnInitedOver();
                Log.Debug("检测版本不用更新!");
                yield break;
            }

            //审核包不用更新文件
            string[] auditPak = updateConfiger.AuditPak;
            string CurrentPak = AppConst.AppName + AppConst.Version;
            foreach (string item in auditPak)
            {
                if (item.Equals(CurrentPak))
                {
                    OnInitedOver();
                    Log.Debug("审核包不用更新!");
                    yield break;
                }
            }
            //检测更新文件
            BaseValueConfigerJson[] files = updateConfiger.JsonData;
            AppConst.valueConfiger.Add(files);
            Log.Debug("需要更新文件个数=" + files.Length);
            bool isResGame = false;
            for (int i = 0; i < files.Length; i++)
            {
                Progress?.Invoke(files.Length, i);
                BaseValueConfigerJson fj = files[i];
                //更新到了CSCofiger 要重新启动一次客户端
                if (fj.dirPath.ToLower().Contains(AppConst.CSConfigerName.ToLower())) { isResGame = true; }
                if (string.IsNullOrEmpty(fj.dirPath)) { continue; }
                if (fj.type != 0) { continue; }
                string fileDirPath = fj.dirPath;
                string fileLocalPath = (dataPath + fileDirPath).Trim();
                string fileDir = Path.GetDirectoryName(fileLocalPath);
                if (!Directory.Exists(fileDir))
                {
                    Directory.CreateDirectory(fileDir);
                }

                string fileWebUrl = url.Replace(AppConst.UpdateConfigerName, string.Empty) + fileDirPath + "?v=" + fj.md5;

                bool canUpdate = !File.Exists(fileLocalPath);
                if (!canUpdate)
                {
                    string remoteMd5 = fj.md5.Trim();
                    string localMd5 = MD5Helper.MD5File(fileLocalPath);
                    canUpdate = !remoteMd5.Equals(localMd5);
                    if (canUpdate)
                        File.Delete(fileLocalPath);
                }
                //找出要更新的文件
                if (canUpdate)
                {   //本地缺少文件
                    HotUpdateFiles.Add(fileWebUrl);
                    HotUpdateFiles.Add(fileLocalPath);
                }
            }

            //unity WWW 下载
            if (HotUpdateFiles.Count > 0)
            {
                int NumCount = 0;
                for (int i = 0; i < HotUpdateFiles.Count; i++)
                {
                    string webFile = HotUpdateFiles[i];
                    string localFile = HotUpdateFiles[i + 1];
                    WWW www = new WWW(webFile);
                    yield return www;
                    //防止下载失败的情况
                    if (!string.IsNullOrEmpty(www.error) || !www.isDone)
                    {
                        NumCount++;
                        if (NumCount < 6) { i--; www.Dispose(); continue; }
                        www.Dispose();
                        NumCount = 0;
                        yield break;
                    }
                    File.WriteAllBytes(localFile, www.bytes);
                    www.Dispose();
                    i = i + 1;
                    yield return new WaitForEndOfFrame();
                }
            }
            AppConst.appInstallConfiger.updateVersion = webHallLuaVer;
            if (isResGame)
            {
                ConfigerHelper.SaveAppInstallConfigerr();
                UnityEngine.SceneManagement.SceneManager.LoadScene("Start");
                yield break;
            }
            OnInitedOver();
        }

        public IEnumerator OnUpdateFailed(string error = "")
        {
            Log.Error(error);
            yield return new WaitForEndOfFrame();
            this.OnInitedOver();
        }

        /// <summary>
        /// 资源初始化结束
        /// </summary>
        public void OnInitedOver()
        {
            Log.Debug($"资源处理结束{TimeHelper.Format()}");
            this.gameObject.AddComponent<Init>();
        }
        /// <summary>
        /// 获取更新配置
        /// </summary>
        public IEnumerator GetUpdateConfiger(UrlInfo uInfo)
        {
            UpdateConfiger updateConfiger = JsonMapper.ToObject<UpdateConfiger>(uInfo.conten);
            string url = uInfo.url;
            url = url.Replace("/" + AppConst.HotUpdateDirectoryName, "").Replace(AppConst.UpdateConfigerName, "");
            updateConfiger.UpdateUrl = url;
            AppConst.WebUrl = url;
            AppConst.updateConfiger = updateConfiger;
            //进行更新处理
            StartCoroutine(OnUpdateResource());
            yield break;
        }

        /// <summary>
        /// 获取appInfoConfiger配置
        /// </summary>
        public IEnumerator GetAppInfoConfiger(string _url)
        {
            string url = _url.Replace(AppConst.UpdateConfigerName, AppConst.AppInfoConfigerName);
            WWW www = new WWW(url);
            yield return www;
            AppInfoConfiger appInfoConfiger;
            if (www.isDone && string.IsNullOrEmpty(www.error))
            {
                if (www.text.Contains(AppConst.AuthCode))
                {
                    appInfoConfiger = JsonMapper.ToObject<AppInfoConfiger>(www.text);
                    AppConst.appInfoConfiger = appInfoConfiger;
                }
                www.Dispose();
                yield break;
            }
            www.Dispose();
            Log.Error("获取web配置<appInfoConfiger>失败!");
            string path = PathHelp.AppHotfixResPath + AppConst.AppInfoConfigerName;
            if (!File.Exists(path))
            {
                Log.Error("获取本地配置<appInfoConfiger>失败!");
                yield break;
            }
            string txt = File.ReadAllText(path);
            appInfoConfiger = JsonMapper.ToObject<AppInfoConfiger>(txt);
            AppConst.appInfoConfiger = appInfoConfiger;

            yield break;
        }

        /// <summary>
        /// 获取valueConfiger配置
        /// </summary>
        public IEnumerator GetValueConfiger(string _url)
        {
            string url = _url.Replace(AppConst.UpdateConfigerName, AppConst.ValueConfigerName);
            WWW www = new WWW(url);
            yield return www;
            ValueConfiger valueConfiger;
            if (www.isDone && string.IsNullOrEmpty(www.error))
            {
                if (www.text.Contains(AppConst.AuthCode))
                {
                    valueConfiger = JsonMapper.ToObject<ValueConfiger>(www.text);
                    AppConst.valueConfiger = valueConfiger;
                }
                www.Dispose();
                yield break;
            }
            www.Dispose();
            Log.Error("获取web配置<valueConfiger>失败!");
            string path = PathHelp.AppHotfixResPath + AppConst.ValueConfigerName;
            if (!File.Exists(path))
            {
                Log.Error("获取本地配置<valueConfiger>失败!");
                yield break;
            }
            string txt = File.ReadAllText(path);
            valueConfiger = JsonMapper.ToObject<ValueConfiger>(txt);
            AppConst.valueConfiger = valueConfiger;
            yield break;
        }

        public struct UrlInfo
        {
            public string url;
            public string conten;
            public string ip;
            public string extend;
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        void OnDestroy()
        {
            ConfigerHelper.SaveAppInfoConfiger();
            ConfigerHelper.SaveValueConfiger();
            ConfigerHelper.SaveAppInstallConfigerr();
        }

    }
}
